/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.tabbedproperties.composites;

import java.util.Iterator;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.BehavioralFeature;
import org.eclipse.uml2.uml.OpaqueBehavior;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.StateMachine;
import org.eclipse.uml2.uml.UMLFactory;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.statemachine.command.UMLDi2ElementDeleteFactory;

/**
 * Composite to deal with the Exit behavior of a State. 
 * 
 * @author David Servat
 * @since 11 apr. 2007
 */
public class StateExitComposite extends DecoratedLabelComposite{

	/**
	 * debug channel.
	 */
	protected static int channel = 501;

	/**
	 * The behavior
	 */
	protected Behavior effect = null;
	
	/**
	 * Default Constructor.
	 */
	public StateExitComposite(){
		super("Exit");
	}
	
	/**
	 * Constructor with given name for label.
	 */
	public StateExitComposite(String name){
		super(name);
	}

	/**
	 * key to access to Activity image.
	 */
	private final static String ACTIVITY_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"Activity.gif";
	
	/**
	 * key to access to StateMachine image.
	 */
	private final static String STATE_MACHINE_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"StateMachine.gif";
	
	/**
	 * key to access to OpaqueBehavior image.
	 */
	private final static String OPAQUE_BEHAVIOR_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"OpaqueBehavior.gif";
	
	/**
	 * The Menu to show all kinds of possible Behavior.
	 */
	private Menu menu;
	
	/**
	 * Various menu items.
	 */
	private MenuItem activityItem, stateMachineItem, opaqueBehaviorItem;
	
	/**
	 * Associated selection listeners.
	 */
	private SelectionListener activityItemListener, stateMachineItemListener, opaqueBehaviorItemListener;
	
	/**
	 * Property page
	 */
	private TabbedPropertySheetPage tabbedPropertySheetPage = null;
	
	/**
	 * Loads a new property page on the effect
	 */
	@Override
	public void editText(){
		//TODO: find a solution to load a new property page
		if(effect != null){
			EditPart selection = null;
			
			Iterator<EditPart> i = LookForElement.getAllOutlineEditParts().iterator();
			while((selection == null) && i.hasNext()){
				EditPart current = i.next() ;
				if((current.getModel() != null) && current.getModel().equals(effect))
					selection = current ;
			}
			if(selection != null){
				tabbedPropertySheetPage.selectionChanged(getActiveEditor(), new StructuredSelection(selection));
			}
		}
	}

	/**
	 * Stores the tabbed property page before handling the call
	 * to createContent
	 */
	public Composite createContent(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage){
		this.tabbedPropertySheetPage = tabbedPropertySheetPage;
		return createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
	}

	/**
	 * Creates contents.
	 * 
	 * @param parent Composite.
	 * @param factory TabbedPropertySheetWidgetFactory.
	 * 
	 * @return specific Composite.
	 */
	@Override
	public Composite createContent(Composite parent, TabbedPropertySheetWidgetFactory factory){
		Composite composite = super.createContent(parent, factory);
				
		//add an additional menu to the add Button
		createMenuForAddButton();
		
		return composite;
	}
	
	/**
	 * Adds an additional menu to the addButton. So user can choose which type of trigger to create.
	 */
	private void createMenuForAddButton(){
		menu = new Menu (addButton);
		
		// add activity menu item
		activityItem = new MenuItem (menu, SWT.PUSH);
		activityItem.setText("Activity");
		activityItem.setImage(PapyrusPlugin.getImage(ACTIVITY_IMAGE));
		activityItem.addSelectionListener(activityItemListener = new AddElementSelectionListener(Activity.class));

		// add stateMachine menu item
		stateMachineItem = new MenuItem (menu, SWT.PUSH);
		stateMachineItem.setText("StateMachine");
		stateMachineItem.setImage(PapyrusPlugin.getImage(STATE_MACHINE_IMAGE));
		stateMachineItem.addSelectionListener(stateMachineItemListener = new AddElementSelectionListener(StateMachine.class));

		// add opaqueBehavior menu item
		opaqueBehaviorItem = new MenuItem (menu, SWT.PUSH);
		opaqueBehaviorItem.setText("OpaqueBehavior");
		opaqueBehaviorItem.setImage(PapyrusPlugin.getImage(OPAQUE_BEHAVIOR_IMAGE));
		opaqueBehaviorItem.addSelectionListener(opaqueBehaviorItemListener = new AddElementSelectionListener(OpaqueBehavior.class));
		
		addButton.setMenu (menu);
	}
	
	/**
	 * Refreshes the composite.
	 */
	@Override
	public void refresh(){
		Assert.isNotNull(element.getUml2Element(), "Passed argument is null");
		Assert.isTrue((element.getUml2Element() instanceof State), "Passed argument is not a State");

		effect = ((State)element.getUml2Element()).getExit();
		if(effect != null){
			if(!text.isDisposed()){
				if(effect instanceof Activity)
					text.setImage(PapyrusPlugin.getImage(ACTIVITY_IMAGE));
				else if(effect instanceof StateMachine)
					text.setImage(PapyrusPlugin.getImage(STATE_MACHINE_IMAGE));
				else if(effect instanceof OpaqueBehavior)
					text.setImage(PapyrusPlugin.getImage(OPAQUE_BEHAVIOR_IMAGE));
				BehavioralFeature specification = effect.getSpecification();
				String effectLabel = effect.getLabel();
				if (specification instanceof Operation) {
					effectLabel = effectLabel + " -> " 
					+ com.cea.papyrus.umlutils.Operation.getSignature((Operation) specification, true);
				}	
				text.setText(effectLabel);
			}
			if(!addButton.isDisposed())
				addButton.setEnabled(false);
			if(!removeButton.isDisposed())
				removeButton.setEnabled(true);
		}
		else{
			if(!text.isDisposed()){
				text.setText("ExitUnset");
				text.setImage(null);
			}
			if(!addButton.isDisposed())
				addButton.setEnabled(true);
			if(!removeButton.isDisposed())
				removeButton.setEnabled(false);
		}
	}

	/**
	 * Executed behavior when addButton pressed.
	 */
	public void addButtonPressed(){
		if(!menu.isDisposed())
			menu.setVisible(true);
	}

	/**
	 * Executed behavior when removeButton pressed.
	 */
	public void removeButtonPressed(){
		Assert.isNotNull(element.getUml2Element(), "Passed argument is null");
		Assert.isTrue((element.getUml2Element() instanceof State), "Passed argument is not a State");

		CompoundCommand command = new CompoundCommand(); 
		
		if(!text.isDisposed())
			text.setText("ExitUnset");
		
		Behavior exit = ((State)element.getUml2Element()).getExit();
		if(exit != null){
			command.add(UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(exit));
			((State)element.getUml2Element()).setExit(null);
		}
				
		//execute command
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(command);
		
		//refresh contents
		refresh();
	}

	/**
	 * Disposes of the composite
	 */
	public void dispose(){
		super.dispose();
		
		if((activityItem != null) && !activityItem.isDisposed())
			activityItem.removeSelectionListener(activityItemListener);
		if((stateMachineItem != null) && !stateMachineItem.isDisposed())
			stateMachineItem.removeSelectionListener(stateMachineItemListener);
		if((opaqueBehaviorItem != null) && !opaqueBehaviorItem.isDisposed())
			opaqueBehaviorItem.removeSelectionListener(opaqueBehaviorItemListener);
	}
	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener{
		
		/**
		 * Constructor with given class.
		 * 
		 * @param elementClass Class
		 */
		public AddElementSelectionListener(Class elementClass){
			this.elementClass = elementClass;
		}
		
		/**
		 * Stored Class.
		 */
		public Class elementClass;

		/**
		 * Does nothing.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/**
		 * What to do when item selected depending on the type of Behavior we want to associate a State to.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetSelected(SelectionEvent e){
			Debug.debug(this, "create a Behavior associated to a "+elementClass.getSimpleName(), channel);
			
			Assert.isNotNull(element.getUml2Element(), "Passed argument is null");
			Assert.isTrue((element.getUml2Element() instanceof State), "Passed argument is not a State");

			//case of an Activity
			if(elementClass.equals(Activity.class)){
				//we need to create a new Activity
				Activity activity = UMLFactory.eINSTANCE.createActivity();
				activity.setName("Exit");
				((State)element.getUml2Element()).setExit(activity);
			}
			//case of a StateMachine
			else if(elementClass.equals(StateMachine.class)){
				//we need to create a new StateMachine
				StateMachine stateMachine = UMLFactory.eINSTANCE.createStateMachine();
				stateMachine.setName("Exit");
				((State)element.getUml2Element()).setExit(stateMachine);
			}
			//case of an OpaqueBehavior
			else if(elementClass.equals(OpaqueBehavior.class)){
				//we need to create a new OpaqueBehavior
				OpaqueBehavior opaqueBehavior = UMLFactory.eINSTANCE.createOpaqueBehavior();
				opaqueBehavior.setName("Exit");
				((State)element.getUml2Element()).setExit(opaqueBehavior);
			}

			//refresh contents
			refresh();
		}
	}

}
